/*
 
 RFA Manager, Refractor2 Archive editor.
 Copyright (C) 2008
 Torsten Kammer         torsten@ferroequinologist.de
 
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 */

#import "MyDocument.h"
#import "RFAArchive.h"
#import "RFATreeNode.h"
#import "ImageAndTextCell.h"
#import "RFABrowserCell.h"
#import "NSStringRFAAdditions.h"
#import "RFATextEditorController.h"
#import "FileSizeFormatter.h"

@interface MyDocument ()

- (void)_updateStatusBarText;
- (void)_updateSearchPredicate;
- (void)_updateSearchPathControl;
- (BOOL)_findLaunchAttributes;
- (void)_openFile:(id)sender;
- (void)_openFileSearch:(id)sender;
- (void)_openFiles:(NSArray *)files;

@end

@implementation MyDocument

- (id)init
{
    self = [super init];
    if (self) {
    
        // Add your subclass-specific initialization here.
        // If an error occurs here, send a [self release] message and return nil.
        
        rootTreeNode = [[RFATreeNode alloc] initAsRoot];
        filterPredicate = [NSPredicate predicateWithValue:NO];
    }
    return self;
}

- (void)makeWindowControllers
{
	NSWindowController *mainWindowController = [[NSWindowController alloc] initWithWindowNibName:@"MyDocument" owner:self];
	[self addWindowController:mainWindowController];
	[mainWindowController setShouldCloseDocument:YES];
}

- (void)windowControllerDidLoadNib:(NSWindowController *) aController
{
    [super windowControllerDidLoadNib:aController];
    
	if ([aController isKindOfClass:[RFATextEditorController class]]) return; // RFATextEditorController knows very well what it's doing on it's own, thank you very much.
	
	[browser setCellClass:[RFABrowserCell class]];
	[browser loadColumnZero]; // Needed to make it use RFABrowserCell for the first entry.
	
	browser.doubleAction = @selector(_openFile:);
	browser.target = self;
	
	outlineView.doubleAction = @selector(_openFile:);
	outlineView.target = self;
	
	searchView.doubleAction = @selector(_openFileSearch:);
	searchView.target = self;
	
    NSSearchFieldCell *searchCell = search.cell;
    searchCell.cancelButtonCell.target = self;
    searchCell.cancelButtonCell.action = @selector(cancelSearch:);
    
    [self willChangeValueForKey:@"tabToShow"];
    [[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObject:[NSNumber numberWithInteger:0] forKey:@"defaultViewMode"]];
    viewMode = [[NSUserDefaults standardUserDefaults] integerForKey:@"defaultViewMode"];
    viewModeControl.selectedSegment = viewMode;
    inSearchMode = NO;
    [self didChangeValueForKey:@"tabToShow"];
    
    searchPathControl.pathComponentCells = [NSArray array];
	
	[fileTreeController addObserver:self forKeyPath:@"selection" options:NSKeyValueObservingOptionNew context:fileTreeController];
	[fileTreeController addObserver:self forKeyPath:@"arrangedObjects" options:NSKeyValueObservingOptionNew context:fileTreeController];
    [searchArrayController addObserver:self forKeyPath:@"selectedObjects" options:NSKeyValueObservingOptionNew context:searchArrayController];
    [[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:@"searchFilenames" options:NSKeyValueObservingOptionNew context:[NSUserDefaults standardUserDefaults]];
    [[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:@"searchFileContents" options:NSKeyValueObservingOptionNew context:[NSUserDefaults standardUserDefaults]];
}

- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    // Checking for context because NSDocument may have other reasons to observe standardUserDefaults. Or not. We'll never know.
    if (context == [NSUserDefaults standardUserDefaults])
        [self _updateSearchPredicate];
    else if (context == searchArrayController)
        [self _updateSearchPathControl];
	else if (context == fileTreeController)
		[self _updateStatusBarText];
    else
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}


- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError
{
	return [archive completeFileData];
}

- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError
{
    archive = [[RFAArchive alloc] initWithData:data error:outError];
    if (!archive) return NO;
    
    // Construct the tree
    for (RFAArchiveEntry *entry in archive.files)
        [rootTreeNode addArchiveEntry:entry];
    
	[self willChangeValueForKey:@"saveAndRunEnabled"];
	canRunFile = [self _findLaunchAttributes];
	[self didChangeValueForKey:@"saveAndRunEnabled"];
	
    return YES;
}

@dynamic rfaTree;
- (RFATreeNode *)rfaTree
{
    // For some reason, there is no firstObject, but since rootTreeNode is unlikely to have
    // more than one child it does not matter.
    return rootTreeNode.children.lastObject;
}

@dynamic archiveEntries;
- (NSArray *)archiveEntries
{
    return archive.files;
}

@dynamic searchPredicate;
- (NSPredicate *)searchPredicate
{
    return filterPredicate;
}

- (IBAction)saveAndRun:(id)sender;
{
	if (!canRunFile) return;
	
	[self saveDocument:self];
	
	NSArray *arguments;
	if (mapName)
		arguments = [NSArray arrayWithObjects:@"+game", modName, @"+level", mapName, @"+restart", @"1", @"+hostServer", @"1", @"+gamePlayMode", @"Conquest", @"+getContentCRC", @"1", nil];
	else
		arguments = [NSArray arrayWithObjects:@"+game", modName, @"+restart", @"1", nil];
	
	[NSTask launchedTaskWithLaunchPath:gamePath arguments:arguments];
}

- (IBAction)selectDisplayMode:(id)sender;
{
    [self willChangeValueForKey:@"tabToShow"];
    viewMode = [sender selectedSegment];
    [[NSUserDefaults standardUserDefaults] setInteger:viewMode forKey:@"defaultViewMode"];
    [self didChangeValueForKey:@"tabToShow"];
}
- (IBAction)search:(id)sender;
{
    if (!inSearchMode)
    {
        [self willChangeValueForKey:@"tabToShow"];
        inSearchMode = YES;
        [viewModeControl setEnabled:NO];
        [self didChangeValueForKey:@"tabToShow"];
    }
    
    [self _updateSearchPredicate];
}
- (IBAction)cancelSearch:(id)sender;
{
    [self willChangeValueForKey:@"tabToShow"];
    inSearchMode = NO;
    [viewModeControl setEnabled:YES];
    search.stringValue = @"";
    [self didChangeValueForKey:@"tabToShow"];
}

@dynamic tabToShow;
- (NSInteger)tabToShow
{
    if (inSearchMode) return 2;
    return viewMode;
}

- (void)_updateSearchPredicate
{
    [self willChangeValueForKey:@"searchPredicate"];
    BOOL searchFilenames = [[NSUserDefaults standardUserDefaults] boolForKey:@"searchFilenames"];
    BOOL searchFileContents = [[NSUserDefaults standardUserDefaults] boolForKey:@"searchFileContents"];
    NSString *searchTerm = search.stringValue;
    if (searchTerm.length == 0 || (!searchFilenames && !searchFileContents))
    {
        filterPredicate = [NSPredicate predicateWithValue:NO];
        return;
    }
    
    NSExpression *searchTermExpression = [NSExpression expressionForConstantValue:searchTerm];
    NSExpression *nameExpression = [NSExpression expressionForKeyPath:@"path.lastPathComponent"];
    NSExpression *contentsExpression = [NSExpression expressionForKeyPath:@"contents"];
    NSPredicate *nameContainsPredicate = [NSComparisonPredicate predicateWithLeftExpression:searchTermExpression
                                                                            rightExpression:nameExpression
                                                                                   modifier:NSDirectPredicateModifier
                                                                                       type:NSInPredicateOperatorType
                                                                                    options:0];
    NSPredicate *contentsContainPredicate = [NSComparisonPredicate predicateWithLeftExpression:searchTermExpression
                                                                               rightExpression:contentsExpression
                                                                                      modifier:NSDirectPredicateModifier
                                                                                          type:NSInPredicateOperatorType
                                                                                       options:0];
    
    
    if (searchFilenames && searchFileContents)
        filterPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:[NSArray arrayWithObjects:nameContainsPredicate, contentsContainPredicate, nil]];
    else if (searchFilenames)
        filterPredicate = nameContainsPredicate;
    else if (searchFileContents)
        filterPredicate = contentsContainPredicate;

    [self didChangeValueForKey:@"searchPredicate"];
}

- (void)_updateSearchPathControl;
{
    NSArray *selected = searchArrayController.selectedObjects;
    if (selected.count != 1) 
        searchPathControl.pathComponentCells = [NSArray array];
    else
    {
		NSArray *pathComponents = [[selected objectAtIndex:0] valueForKeyPath:@"path.pathComponents"];
		NSMutableArray *pathComponentCells = [NSMutableArray array];
		NSImage *folderImage = [[NSWorkspace sharedWorkspace] iconForFile:@"/System/Library"];
		for (NSString *component in pathComponents)
		{
			NSPathComponentCell *cell = [[NSPathComponentCell alloc] initTextCell:component];
			cell.image = folderImage;
			cell.controlSize = NSSmallControlSize;
			cell.font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]];
			[pathComponentCells addObject:cell];
		}
		[pathComponentCells.lastObject setImage:[[NSWorkspace sharedWorkspace] iconForFileType:[pathComponentCells.lastObject stringValue]]];
		searchPathControl.pathComponentCells = pathComponentCells;
    }
	[self _updateStatusBarText];
}

@synthesize saveAndRunEnabled=canRunFile;

- (BOOL)_findLaunchAttributes;
{
	if (!self.fileURL.isFileURL) return NO;
	NSArray *pathComponents = self.fileURL.path.pathComponents;
	NSUInteger length = pathComponents.count;

	mapName = nil;
	modName = nil;
	gamePath = nil;
	
	// There are three cases: Either it's in Archives, or in Archives/bf1942, or in Archvies/bf1942/levels
	// First: Is it in levels?
	NSString *folder = [pathComponents objectAtIndex:length - 2];
	if ([folder isEqual:@"levels"])
	{
		if (length < 7) return NO;
		mapName = [pathComponents objectAtIndex:length - 1];
		mapName = [mapName substringToIndex:[mapName length] - 4];
		if (![[pathComponents objectAtIndex:length - 3] isCaseInsensitiveEqual:@"bf1942"]) return NO;
		if (![[pathComponents objectAtIndex:length - 4] isCaseInsensitiveEqual:@"Archives"]) return NO;
		modName = [pathComponents objectAtIndex:length - 5];
		if (![[pathComponents objectAtIndex:length - 6] isCaseInsensitiveEqual:@"Mods"]) return NO;
		if (![[pathComponents objectAtIndex:length - 7] isCaseInsensitiveEqual:@"Data"]) return NO;
		NSArray *resultArray = [[pathComponents subarrayWithRange:NSMakeRange(0, length - 7)] arrayByAddingObjectsFromArray:[NSArray arrayWithObjects:@"Battlefield 1942.app", @"Contents", @"MacOS", @"Battlefield 1942", nil]];
		gamePath = [NSString pathWithComponents:resultArray];
		return [[NSFileManager defaultManager] fileExistsAtPath:gamePath];
	}
	else if ([folder isEqual:@"bf1942"])
	{
		if (length < 6) return NO;
		if (![[pathComponents objectAtIndex:length - 3] isCaseInsensitiveEqual:@"Archives"]) return NO;
		modName = [pathComponents objectAtIndex:length - 4];
		if (![[pathComponents objectAtIndex:length - 5] isCaseInsensitiveEqual:@"Mods"]) return NO;
		if (![[pathComponents objectAtIndex:length - 6] isCaseInsensitiveEqual:@"Data"]) return NO;
		NSArray *resultArray = [[pathComponents subarrayWithRange:NSMakeRange(0, length - 6)] arrayByAddingObjectsFromArray:[NSArray arrayWithObjects:@"Battlefield 1942.app", @"Contents", @"MacOS", @"Battlefield 1942", nil]];
		gamePath = [NSString pathWithComponents:resultArray];
		return [[NSFileManager defaultManager] fileExistsAtPath:gamePath];
	}
	else if ([folder isEqual:@"Archives"])
	{
		if (length < 5) return NO;
		modName = [pathComponents objectAtIndex:length - 3];
		if (![[pathComponents objectAtIndex:length - 4] isCaseInsensitiveEqual:@"Mods"]) return NO;
		if (![[pathComponents objectAtIndex:length - 5] isCaseInsensitiveEqual:@"Data"]) return NO;
		NSArray *resultArray = [[pathComponents subarrayWithRange:NSMakeRange(0, length - 5)] arrayByAddingObjectsFromArray:[NSArray arrayWithObjects:@"Battlefield 1942.app", @"Contents", @"MacOS", @"Battlefield 1942", nil]];
		gamePath = [NSString pathWithComponents:resultArray];
		return [[NSFileManager defaultManager] fileExistsAtPath:gamePath];
	}
	return NO;
}

- (void)_openFile:(id)sender;
{
	[self _openFiles:[fileTreeController valueForKeyPath:@"selectedObjects.path"]];
}
- (void)_openFileSearch:(id)sender;
{
	[self _openFiles:[searchArrayController valueForKeyPath:@"selectedObjects.path"]];
}
- (void)_openFiles:(NSArray *)files;
{
	NSArray *textFileExtensions = [RFAArchiveEntry extensionsToTreatAsText];
	for (NSString *path in files)
	{
		RFAArchiveEntry *entry = [archive archiveEntryWithPath:path];
		if (!entry) continue;
		if ([textFileExtensions containsObject:path.pathExtension])
		{
			RFATextEditorController *textController = [[RFATextEditorController alloc] initWithArchiveEntry:entry];
			[self addWindowController:textController];
			[[textController window] makeKeyAndOrderFront:self];
		}
	}
}

- (void)_updateStatusBarText
{
	if (inSearchMode)
	{
		if (searchArrayController.selectedObjects.count == 0)
		{
			if ([searchArrayController.arrangedObjects count] == 1)
				statusBar.stringValue = [NSString stringWithFormat:NSLocalizedString(@"1 object, %@", @"status bar for no selection, single object"), [FileSizeFormatter stringForNumber:[searchArrayController valueForKeyPath:@"arrangedObjects.@sum.data.length"]]];
			else
				statusBar.stringValue = [NSString stringWithFormat:NSLocalizedString(@"%lu objects, %@", @"status bar for no selection, multiple objects"), (unsigned long) [searchArrayController.arrangedObjects count], [FileSizeFormatter stringForNumber:[searchArrayController valueForKeyPath:@"arrangedObjects.@sum.data.length"]]];
		}
		else if (searchArrayController.selectedObjects.count == 1)
			statusBar.stringValue = [NSString stringWithFormat:NSLocalizedString(@"1 object of %lu selected, %@", @"status bar for single selection"), (unsigned long) [searchArrayController.arrangedObjects count], [FileSizeFormatter stringForNumber:[searchArrayController valueForKeyPath:@"selectedObjects.@sum.data.length"]]]; 
		else
			statusBar.stringValue = [NSString stringWithFormat:NSLocalizedString(@"%lu objects of %lu selected, %@", @"status bar for multiple selection"), (unsigned long) searchArrayController.selectedObjects.count, (unsigned long) [searchArrayController.arrangedObjects count], [FileSizeFormatter stringForNumber:[searchArrayController valueForKeyPath:@"selectedObjects.@sum.data.length"]]];
	}
	else
	{
		if (fileTreeController.selectedNodes.count == 0)
			statusBar.stringValue = [NSString stringWithFormat:NSLocalizedString(@"1 object, %@", @"status bar for no selection, single object"), [FileSizeFormatter stringForNumber:[self valueForKeyPath:@"rfaTree.size"]]];
		else if (fileTreeController.selectedNodes.count == 1)
		{
			if ([[fileTreeController.selectedObjects objectAtIndex:0] isLeaf])
				statusBar.stringValue = [NSString stringWithFormat:NSLocalizedString(@"1 object of %lu selected, %@", @"status bar for single selection"), (unsigned long) [[[[fileTreeController.selectedNodes objectAtIndex:0] parentNode] childNodes] count], [FileSizeFormatter stringForNumber:[fileTreeController valueForKeyPath:@"selectedObjects.@sum.size"]]]; 
			else
			{
				if ([[[fileTreeController.selectedNodes objectAtIndex:0] childNodes] count] == 1)
					statusBar.stringValue = [NSString stringWithFormat:NSLocalizedString(@"1 object, %@", @"status bar for no selection, single object"), [FileSizeFormatter stringForNumber:[fileTreeController valueForKeyPath:@"selectedObjects.@sum.size"]]];
				else
					statusBar.stringValue = [NSString stringWithFormat:NSLocalizedString(@"%lu objects, %@", @"status bar for no selection, multiple objects"), (unsigned long) [[[fileTreeController.selectedNodes objectAtIndex:0] childNodes] count], [FileSizeFormatter stringForNumber:[fileTreeController valueForKeyPath:@"selectedObjects.@sum.size"]]];
			}
		}
		else
			statusBar.stringValue = [NSString stringWithFormat:NSLocalizedString(@"%lu objects of %lu selected, %@", @"status bar for multiple selection"), (unsigned long) fileTreeController.selectedNodes.count, (unsigned long) [[[[fileTreeController.selectedNodes objectAtIndex:0] parentNode] childNodes] count], [FileSizeFormatter stringForNumber:[fileTreeController valueForKeyPath:@"selectedObjects.@sum.size"]]];
	}
}

@end
